<?php
/*********************************************
  Dragonfly CMS, Copyright (c) 2004 by CPGNuke Dev Team
  http://dragonflycms.org
  Released under GNU GPL version 2 or any later version

  $Source$
  $Revision$
  $Author$
  $Date$
**********************************************/

abstract class JS
{

	protected static $toClientPattern = array('#^i:([a-z0-9_]+)$#i', '#^t:([a-z0-9_]+):([a-z0-9_]+)$#i', '#^m:([a-z0-9_]+):([a-z0-9_]+)$#i');
	protected static $toClientReplace = array('includes/javascript/$1.js', 'themes/$1/javascript/$2.js', 'modules/$1/javascript/$2.js');

	protected static $toTplPattern = array('#^includes/javascript/([a-z0-9_]+)\.js$#i', '#^themes/([a-z0-9_]+)/javascript/([a-z0-9_]+)\.js$#i', '#^modules/([a-z0-9_]+)/javascript/([a-z0-9_]+)\.js$#i');
	protected static $toTplReplace = array('i:$1', 't:$1:$2', 'm:$1:$2');

	private static $files = array();
	private static $inline = array();
	private static $mtime = 0;
	private static $canAdd = true;
	private static $theme;

	final public static function add($file)
	{
		if (self::$canAdd && self::filter($file, 'toTpl') && file_exists(BASEDIR .$file)) {
			self::$files[] = preg_replace(self::$toTplPattern, self::$toTplReplace, $file);
		}
	}

	final public static function inline($str)
	{
		if (self::$canAdd && is_string($str) && $str) {
			self::$inline[] = $str;
		}
	}

	final public static function flushToTpl()
	{
		$return = '';
		self::$files = array_values(array_unique(self::$files));

		if (empty(self::$files)) return $return;
		$return .= '<script type="text/javascript" src="?js=' .implode(';', self::$files) ."\"></script>".DF_EOL;
		self::$files = array();

		if (!empty(self::$inline)) {
			$return .= '<script type="text/javascript">' .DF_EOL;
			while ($file = array_shift(self::$inline)) {
				$return .= self::minify($file) .DF_EOL;
			}
			$return .= '</script>' .DF_EOL;
		}
		return $return;
	}

	final public static function request()
	{
		self::$canAdd = false;
		if (!preg_match('#^[a-z0-9_\-;:]+$#iD', $_GET['js'])) return false;
		$items = explode(';', $_GET['js']);
		for ($i=0, $c=count($items); $i<$c; ++$i) {
			if (self::filter($items[$i], 'toClient')) {
				$file = BASEDIR .preg_replace(self::$toClientPattern, self::$toClientReplace, $items[$i]);
				if (is_file($file)) {
					self::$files[] = $file;
					self::$mtime = max(self::$mtime, filemtime($file));
				} else {
					trigger_error($items[$i] .' could not be found', E_USER_WARNING);
				}
			}
		}
		self::$files = array_values(array_unique(self::$files));
		return !empty(self::$files);
	}

	# http://blog.stevenlevithan.com/archives/match-quoted-string speed?
//	static protected $str_re = '#(["\'])(?:.*[^\\\\]+)*(?:(?:\\\\{2})*)+\1#xU';
//	static protected $str_re = '#(["\'])(?:\\\\?[^\n])*?\1#s';
	static protected $str_re = '#"[^\n"\\\\]*(?:\\\\.[^\n"\\\\]*)*"|\'[^\n\'\\\\]*(?:\\\\.[^\n\'\\\\]*)*\'|/[^\s/\\\\]+(?:\\\\.[^\n/\\\\]*)*/[gmi]*#';

	final public static function minify($str)
	{
		if (DF_MODE_DEVELOPER) return $str;
		if ($str)
		{
			$str = str_replace("\r", '', $str);
			$str = preg_replace('#(^|\s+)//.*#m', '', $str);
			$str = preg_replace('#(^|\s+)/\*[^@].*?\*/#s', '', $str); // Strip comments but keep IE specific stuff
			$str = preg_replace('#console\.[a-z]+\(((?(?!\);).)*)\);#', '', $str);
			preg_match_all(self::$str_re, $str, $strings);
			$strings = $strings[0];
			$str = preg_split(self::$str_re, $str);
//			$str = str_replace("\n", '', $str);
			$str = preg_replace('#\n+#', '', $str);
			$str = preg_replace('#\s+#', ' ', $str);
			# case|else if|function|in|new|return|typeof|var
			$str = preg_replace('#\s*([&%/\[\]{}\(\)\|\+!\?\-=:;,><\.\*]+)\s*#', '$1', $str);
//			$str = preg_replace('#\s*(\$?[^a-z_])\s*#si', '$1', $str);
			$str = str_replace(';}', '}', $str);
			$c = 1;
			while ($c) { $str = preg_replace('#var([^;]+);var#', 'var$1,', $str, -1, $c); }
			$c = count($strings);
			for ($i = 0; $i < $c; ++$i) { $str[$i] .= $strings[$i]; }
			return implode('', $str);
		}
		return $str;
	}

	final public static function flushToClient()
	{
		$ETag = md5(implode(';', self::$files).self::$mtime);
		$gzip = GZIP_OUT ? '.gz' : '';
		$cachedFile = CACHE_PATH .'js#' .$ETag;
		$life_time = (DF_MODE_DEVELOPER ? 0 : DF_HTTP_CACHE_EXPIRE);
		if ('1.1' > Dragonfly_Net_Http::$protocolVersion) {
			header('Cache-Control: public, s-maxage=' .$life_time);
		} else {
			header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time() + $life_time));
		}
		header('Content-Type: text/javascript; charset=utf-8');
		if (!CPG_DEBUG && is_file($cachedFile .$gzip) && filemtime($cachedFile .$gzip) > self::$mtime) {
			if ($gzip) {
				header('Vary: Accept-Encoding');
				header('Content-Encoding: gzip');
			}
			header('Content-Length:' .filesize($cachedFile .$gzip));
			if ($status = Dragonfly_Net_Http::entityCache($ETag, self::$mtime)) {
				Dragonfly_Net_Http::headersFlush($status);
			} else if ('GET' === $_SERVER['REQUEST_METHOD']) {
				readfile($cachedFile .$gzip);
			}
			exit;
		}
		$buffer = '';
		foreach (self::$files as $file) {
			$buffer .= self::minify(file_get_contents($file));
		}
		if (GZIPSUPPORT && $gz = gzopen($cachedFile .'.gz','w9')) {
			gzwrite($gz, $buffer);
			gzclose($gz);
		}
		file_put_contents($cachedFile, $buffer);
		if (!CPG_DEBUG && (is_file($cachedFile .$gzip) || !$gzip)) {
			if ($gzip) header('Content-Encoding: gzip');
			header('Content-Length:' .filesize($cachedFile .$gzip));
			if ('GET' === $_SERVER['REQUEST_METHOD']) exit(readfile($cachedFile .$gzip));
		} else {
			ob_start('ob_gzhandler');
			if ('GET' === $_SERVER['REQUEST_METHOD']) exit($buffer);
		}
		exit;
	}

	private static function filter($str, $mode)
	{
		switch ($mode) {
			case 'toTpl':
				$pattern = self::$toTplPattern;
			break;
			case 'toClient':
				$pattern = self::$toClientPattern;
			break;
			default: return;
		}
		for ($i=0, $c=count($pattern); $i<$c; ++$i) {
			if (1 === preg_match($pattern[$i], $str, $match)) {
				if (!self::$theme) self::$theme = $match[1];
				return true;
			}
		}
		return false;
	}

}
